﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGinnBPM.Lib.Schema;
using NGinnBPM.Lib.Interfaces;
using NGinnBPM.Lib.Interfaces.Worklist;
using NGinnBPM.Lib.Data;
using NGinnBPM.Lib.Exceptions;
using NLog;
using System.Runtime.Serialization;
using NGinnBPM.Lib.Operations.ExternalServices;
using NGinnBPM.Runtime.Messages;
using NGinnBPM.MessageBus;

namespace NGinnBPM.Runtime.Tasks
{
    [Serializable]
    [DataContract]
    public class ManualTaskInstance : AtomicTaskInstance
    {
        private string _title;

        [DataMember]
        [TaskParameter(IsInput = true, Required = false, DynamicAllowed = true)]
        public string Title
        {
            get { return _title; }
            set { _title = value; }
        }
        private string _assigneeId;

        [DataMember]
        [TaskParameter(IsInput = true, Required = false, DynamicAllowed = true)]
        public string AssigneeId
        {
            get { return _assigneeId; }
            set { _assigneeId = value; }
        }
        private string _assigneeGroup;

        [DataMember]
        [TaskParameter(IsInput = true, Required = false, DynamicAllowed = true)]
        public string AssigneeGroup
        {
            get { return _assigneeGroup; }
            set { _assigneeGroup = value; }
        }
        private string _description;

        [DataMember]
        [TaskParameter(IsInput = true, Required = false, DynamicAllowed = true)]
        public string Description
        {
            get { return _description; }
            set { _description = value; }
        }

        [DataMember]
        [TaskParameter(IsInput = true, Required = false, DynamicAllowed = true)]
        public string DescriptionHtml { get; set; }

        /// <summary>
        /// Estimated task start date.
        /// </summary>
        [DataMember]
        [TaskParameter(IsInput = true, Required = false, DynamicAllowed = true)]
        public DateTime? StartDate { get; set; }

        /// <summary>
        /// Task due date.
        /// </summary>
        [DataMember]
        [TaskParameter(IsInput = true, Required = false, DynamicAllowed = true)]
        public DateTime? Deadline { get; set; }

        [DataMember]
        [TaskParameter(IsInput = true, Required = false, DynamicAllowed = true)]
        public DateTime? PlannedStart { get; set; }

        [DataMember]
        [TaskParameter(IsInput = true, Required = false, DynamicAllowed = true)]
        public DateTime? PlannedEnd { get; set; }


        /// <summary>
        /// Worklist service to be used
        /// </summary>
        [DataMember]
        [TaskParameter(IsInput = true, Required = false, DynamicAllowed = true)]
        public string WorklistService { get; set; }

        /// <summary>
        /// Worklist service url.
        /// If specified, the service will be called directly
        /// using http/json
        /// </summary>
        [DataMember]
        [TaskParameter(IsInput=true, Required=false, DynamicAllowed=true)]
        public string WorklistUrl { get; set; }

        [DataMember]
        [TaskParameter(IsInput = false)]
        public string WorkItemId { get; set; }
        /// <summary>
        /// Resource manager service name
        /// </summary>
        [DataMember]
        [TaskParameter(IsInput = true, Required = false, DynamicAllowed = true)]
        public string ResourceManager { get; set; }
        
        /// <summary>
        /// Id of person who completed the task
        /// </summary>
        [DataMember]
        [TaskParameter(IsInput = false)]
        public string CompletedBy { get; set; }

        [DataMember]
        [TaskParameter(IsInput = false)]
        public DateTime? CompletedDate { get; set; }

        private IUserDataRepository GetUserDataRepository()
        {
            IUserDataRepository repo;
            if (ResourceManager != null && ResourceManager.Length > 0)
                repo = Context.ServiceLocator.GetInstance<IUserDataRepository>(ResourceManager);
            else
                repo = null; // Context.ServiceLocator.GetInstance<IUserDataRepository>();
            return repo;
        }

        private IWorkListService GetWorklistService()
        {
            IWorkListService ws = null;
            if (WorklistService != null && WorklistService.Length > 0)
                ws = Context.ServiceLocator.GetInstance<IWorkListService>(WorklistService);
            else
                ws = Context.ServiceLocator.GetInstance<IWorkListService>();
            if (ws == null)
                throw new TaskRuntimeException("Worklist service not found: " + WorklistService).SetInstanceId(InstanceId).SetTaskId(TaskId);
            return ws;
        }

        private CreateWorkItem PrepareRequest()
        {
            IUserDataRepository repo = GetUserDataRepository();
            CreateWorkItem wi = new CreateWorkItem();
            wi.CorrelationId = this.InstanceId;
            wi.InstanceId = InstanceId;
            wi.TaskId = TaskId;
            wi.Title = Title;
            wi.Description = Description;
            wi.AssigneeGroupId = AssigneeGroup;
            if (repo != null && AssigneeGroup != null) wi.AssigneeGroupId = repo.SelectGroup(wi.AssigneeGroupId);
            wi.AssigneeId = AssigneeId;
            if (repo != null && AssigneeId != null) wi.AssigneeId = repo.SelectPerson(AssigneeId);
            wi.ProcessDefinitionId = ProcessDefinitionId;
            wi.ProcessInstanceId = ProcessInstanceId;
            wi.TaskData = TaskData;
            wi.DescriptionHtml = DescriptionHtml;
            wi.ProcessStartDate = this.ProcessStartDate;
            wi.Deadline = this.Deadline;
            wi.PlannedStartDate = this.PlannedStart;
            wi.PlannedEndDate = this.PlannedEnd;
            wi.ExtDocumentId = this.ExtDocumentId;

            if (!string.IsNullOrEmpty(WorklistUrl))
            {
                var c = Context.ServiceLocator.GetInstance<IConfigParameterProvider>();
                var surl = c.GetString("NGinnBPM.ManualTask.CallbackUrl", null);
                if (string.IsNullOrEmpty(surl))
                {
                    surl = c.GetString("NGinnBPM.MessageBus.WebServer.Endpoint", "");
                    surl = surl.Replace("http://+", "http://" + Environment.MachineName);
                    if (surl.EndsWith("/")) surl += "/";
                    surl += "call/";
                }
                wi.CallbackUrl = surl;
            }
            return wi;
        }

        public override void Enable(Dictionary<string, object> inputData)
        {
            base.Enable(inputData);
            CreateWorkItem cwi = PrepareRequest();
            if (!string.IsNullOrEmpty(WorklistUrl))
            {
                ServiceClient sc = new ServiceClient { BaseUrl = WorklistUrl };
                var ret = sc.CallService<CreateWorkItemResponse>(cwi);
                WorkItemId = ret.WorkItemId;
            }
            else
            {
                IWorkListService ws = GetWorklistService();
                WorkItemId = ws.CreateWorkItem(cwi);
            }
            log.Info("Created work item. Returned ID: {0}", WorkItemId);
        }

        public override void Cancel()
        {
            if (!string.IsNullOrEmpty(WorklistUrl))
            {
                ServiceClient sc = new ServiceClient { BaseUrl = WorklistUrl };
                var ret = sc.CallService<CancelWorkItemResponse>(new CancelWorkItem { TaskInstanceId = InstanceId, WorkitemId = this.WorkItemId });
            }
            else
            {
                IWorkListService ws = GetWorklistService();
                ws.CancelWorkItem(WorkItemId, InstanceId);
            }
            base.Cancel();
        }

        public override void Fail(string errorInformation)
        {
            if (!string.IsNullOrEmpty(WorklistUrl))
            {
                ServiceClient sc = new ServiceClient { BaseUrl = WorklistUrl };
                var ret = sc.CallService<CancelWorkItemResponse>(new CancelWorkItem { TaskInstanceId = InstanceId, WorkitemId = this.WorkItemId });
            }
            else
            {
                IWorkListService ws = GetWorklistService();
                ws.CancelWorkItem(WorkItemId, InstanceId);
            }
            base.Fail(errorInformation);
        }

        public override void Complete(string finishedBy, Dictionary<string, object> updatedData)
        {
            base.Complete(finishedBy, updatedData);

            Context.MessageBus.Notify(new WorkItemCompleted { TaskInstanceId = this.InstanceId, WorkItemId = this.WorkItemId, WorklistServiceName = this.WorklistService, WorklistUrl = this.WorklistUrl });
        }
    }
}
